import {
	EVENT_PICK_SHOW, EVENT_PICK_HIDE, EVENT_SHOW, EVENT_TEAM_SHOW, EVENT_TEAM_HIDE,
	EVENT_BAN_SHOW, EVENT_PANEL_CONTENT, EVENT_COMPLETE, EVENT_ASSET_RELOAD,
	EVENT_ARROW, EVENT_TIMER_RESET, EVENT_TIMER_PAUSE, EVENT_SEAT,
	EVENT_SEAT_INFO_SHOW, EVENT_SEAT_INFO_HIDE,
} from './events';


class BanPick {
	// 基础参数
	teamLName = "DK";
	teamRName = "EDG";
	teamLDesc = "COACHES: KKOMA, POOHMANDU";
	teamRDesc = "COACHES: MAOKAI, KENZHU";
	teamLScore = "0";
	teamRScore = "0";

	patch = "PATCH 11.19";

	players = {
		PL1: "KHAN",
		PL2: "CANYON",
		PL3: "SHOWMAKER",
		PL4: "GHOST",
		PL5: "BERYL",
		PR1: "FLANDRE",
		PR2: "JIEJIE",
		PR3: "SCOUT",
		PR4: "VIPER",
		PR5: "MEIKO",
	};

	// 可变参数
	seatShow = {
		PL1: false, PL2: false, PL3: false, PL4: false, PL5: false,
		PR1: false, PR2: false, PR3: false, PR4: false, PR5: false,
	};
	/** @type {string[]} 指哪些位置 */
	seatArrow = [];
	/** 各个位置选了啥 */
	seatSelect = {
		BL1: '', BL2: '', BL3: '', BL4: '', BL5: '',
		BR1: '', BR2: '', BR3: '', BR4: '', BR5: '',
		PL1: '', PL2: '', PL3: '', PL4: '', PL5: '',
		PR1: '', PR2: '', PR3: '', PR4: '', PR5: '',
	};

	/** 一共多少时间, ms */
	timeLimit = 30000;
	/** 基准时间 */
	baseInstant = 0;
	/** 暂停的时间戳 */
	timePaused = 0;
	/**
	 * 计算当前的时间轴, 返回剩余的百分比.
	 * @returns {number} 0 ~ 1
	 */
	calcCurrentTimeProcess() {
		if (this.baseInstant == 0) {
			return 1;
		}
		const now = (this.timePaused == 0) ? new Date().getTime() : this.timePaused;
		const duration = now - this.baseInstant;
		if (duration >= this.timeLimit) {
			return 0;
		}
		return 1 - duration / this.timeLimit;
	}

	timePause() {
		if (this.timePaused) return;
		this.timePaused = new Date().getTime();
	}

	timeResume() {
		if (!this.timePaused) return;
		const now = new Date().getTime();
		const duration = this.timePaused - this.baseInstant;
		this.baseInstant = now - duration;
		this.timePaused = 0;
	}

	timeReset(timeLimit = this.timeLimit) {
		this.timeLimit = timeLimit;
		this.baseInstant = new Date().getTime();
		if (this.timePaused) {
			this.timePaused = this.baseInstant;
		}
	}

	constructor(arg) {
		this.context = arg.context;

		this.context.subscribeOne(EVENT_TIMER_RESET,
				(_, param) => this.timeReset(param.ms),
				`banPick#${EVENT_TIMER_RESET}`);
		this.context.subscribeOne(EVENT_TIMER_PAUSE,
				(_, param) => param.pause ? this.timePause() : this.timeResume(),
				`banPick#${EVENT_TIMER_PAUSE}`);
	}

	collectInfo() {
		return {
			team   : [this.teamLName, this.teamRName],
			desc   : [this.teamLDesc, this.teamRDesc],
			score  : [this.teamLScore,this.teamRScore],
			patch  : this.patch,
			players: this.players,
			// 这三个不能通过 assetsLoad 直接改, 只能用 action
			show   : this.seatShow,
			arrow  : this.seatArrow,
			hero   : this.seatSelect,
		}
	}

	assetsLoad(arg) {
		if (arg.team) {
			this.teamLName = arg.team[0];
			this.teamRName = arg.team[1];
		}
		if (arg.desc) {
			this.teamLDesc = arg.desc[0];
			this.teamRDesc = arg.desc[1];
		}
		if (arg.score) {
			this.teamLScore = arg.score[0];
			this.teamRScore = arg.score[1];
		}
		if (arg.patch != null) this.patch = arg.patch;
		if (arg.players != null) Object.assign(this.players, arg.players);
		this.context.releaseEvent(EVENT_ASSET_RELOAD);
	}

	// 可以改写
	onUpdated() {}
}

class Replayer {
	/**
	 * @param {BanPick} bp
	 */
	constructor(bp) {
		this.bp = bp;
		this.baseTime = 0;
		/** @type {Action[]} */
		this.actions = [];
		this.load(defaultActions());
		this.currentIndex = 0;

		this.tick = this.tick.bind(this);
		this.intervalHandler = setInterval(this.tick, 20);
	}

	load(actions) {
		this.actions = actions.slice();
		this.actions.sort((a, b) => a.ms - b.ms);
	}

	run() {
		this.baseTime = new Date().getTime();
		this.currentIndex = 0;
	}

	stop() {
		this.baseTime = 0;
	}

	tick() {
		if (this.baseTime === 0) {
			return;
		}
		const duration = new Date().getTime() - this.baseTime;
		let dirty = false;
		for (; this.currentIndex < this.actions.length; this.currentIndex++) {
			let nextAction = this.actions[this.currentIndex];
			if (duration < nextAction.ms) {
				break;
			}
			nextAction.handle(this.bp);
			dirty = true;
		}

		if (dirty) this.bp.onUpdated();
	}

	actNow(action) {
		action.handle(this.bp);
	}

	collectInfo() {
		return {
			actions : this.actions.map(action => {
				return {
					name : action.name,
					time : action.ms,
					arg  : action.arg
				}
			}),
			pointer : this.currentIndex,
			play    : this.baseTime != 0,
		}
	}
}

function buildAction(name, time = 0, arg = null) {
	switch (name) {
		case EVENT_PICK_SHOW: return new SeatPickShowAction(arg.seats, time);
		case EVENT_PICK_HIDE: return new SeatPickHideAction(arg.seats, time);
		case EVENT_ARROW: return new ArrowAction(arg.seats, time);
		case EVENT_SEAT: return new SelectAction(arg.seat, arg.hero == null ? '' : arg.hero, time);

		default: return new Action(name, time, arg);
	}
}

function defaultActions() {
	return [
		buildAction(EVENT_TIMER_RESET, 0, { ms: 30000 }),
		buildAction(EVENT_SHOW, 0),
		buildAction(EVENT_TEAM_SHOW, 400),
		buildAction(EVENT_TEAM_HIDE, 7000),
		buildAction(EVENT_BAN_SHOW, 7200),
		buildAction(EVENT_PICK_SHOW, 7300, { seats: ['PL1', 'PR1'] }),
		buildAction(EVENT_PICK_SHOW, 7400, { seats: ['PL2', 'PR2'] }),
		buildAction(EVENT_PICK_SHOW, 7500, { seats: ['PL3', 'PR3'] }),
		buildAction(EVENT_PICK_SHOW, 7600, { seats: ['PL4', 'PR4'] }),
		buildAction(EVENT_PICK_SHOW, 7700, { seats: ['PL5', 'PR5'] }),
		buildAction(EVENT_PANEL_CONTENT, 7400, { content: 'battle' }),
		buildAction(EVENT_SEAT, 200, { seat: 'BL1', hero: 'lol/Aphelios' }),
		buildAction(EVENT_SEAT, 300, { seat: 'BR1', hero: 'lol/Yuumi' }),
		buildAction(EVENT_SEAT, 400, { seat: 'BL2', hero: 'lol/Nami' }),
		buildAction(EVENT_ARROW, 400, { seats: ['BR2'] }),
		buildAction(EVENT_SEAT, 8000, { seat: 'BR2', hero: 'lol/TwistedFate' }),
		buildAction(EVENT_TIMER_RESET, 8000, { ms: 30000 }),
		buildAction(EVENT_ARROW, 8000, { seats: ['BL3'] }),
		buildAction(EVENT_SEAT, 11500, { seat: 'BL3', hero: 'lol/Lissandra' }),
		buildAction(EVENT_TIMER_RESET, 11500, { ms: 30000 }),
		buildAction(EVENT_ARROW, 11500, { seats: ['BR3'] }),
		buildAction(EVENT_SEAT, 30000, { seat: 'BR3', hero: 'lol/LeeSin' }),
		buildAction(EVENT_TIMER_RESET, 30000, { ms: 30000 }),
		buildAction(EVENT_ARROW, 30000, { seats: ['PL1'] }),
		buildAction(EVENT_SEAT, 35000, { seat: 'PL1', hero: 'lol/Leblanc' }),
		buildAction(EVENT_COMPLETE, 37000, { seats: ['PL1'] }),
		buildAction(EVENT_ARROW, 37000, { seats: ['PR1'] }),
		buildAction(EVENT_TIMER_RESET, 37000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 39000, { seats: ['PL1'] }),
		buildAction(EVENT_SEAT, 42000, { seat: 'PR1', hero: 'lol/Jhin' }),
		buildAction(EVENT_SEAT_INFO_HIDE, 42500, { seats: ['PL1'] }),
		buildAction(EVENT_COMPLETE, 43000, { seats: ['PR1'] }),
		buildAction(EVENT_ARROW, 43000, { seats: ['PR2'] }),
		buildAction(EVENT_TIMER_RESET, 43000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 47000, { seats: ['PR1'] }),
		buildAction(EVENT_SEAT, 47000, { seat: 'PR2', hero: 'lol/Jarvaniv' }),
		buildAction(EVENT_COMPLETE, 50000, { seats: ['PR2'] }),
		buildAction(EVENT_ARROW, 50000, { seats: ['PL2'] }),
		buildAction(EVENT_TIMER_RESET, 50000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 57000, { seats: ['PR2'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 61000, { seats: ['PR2'] }),
		buildAction(EVENT_SEAT, 74000, { seat: 'PL2', hero: 'lol/Ziggs' }),
		buildAction(EVENT_COMPLETE, 76000, { seats: ['PL2'] }),
		buildAction(EVENT_ARROW, 76000, { seats: ['PL3'] }),
		buildAction(EVENT_TIMER_RESET, 76000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 78000, { seats: ['PL2'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 82500, { seats: ['PL2'] }),
		buildAction(EVENT_SEAT, 97500, { seat: 'PL3', hero: 'lol/Rakan' }),
		buildAction(EVENT_COMPLETE, 100000, { seats: ['PL3'] }),
		buildAction(EVENT_ARROW, 100000, { seats: ['PR3'] }),
		buildAction(EVENT_TIMER_RESET, 100000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 102000, { seats: ['PL2'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 107000, { seats: ['PL2'] }),
		buildAction(EVENT_SEAT, 120000, { seat: 'PR3', hero: 'lol/Orianna' }),
		buildAction(EVENT_SEAT, 121000, { seat: 'PR3', hero: 'lol/Ryze' }),
		buildAction(EVENT_COMPLETE, 121500, { seats: ['PR3'] }),
		buildAction(EVENT_ARROW, 121500, { seats: ['BR4'] }),
		buildAction(EVENT_TIMER_RESET, 121500, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 123000, { seats: ['PR3'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 127000, { seats: ['PR3'] }),
		buildAction(EVENT_SEAT, 144000, { seat: 'BR4', hero: 'lol/Talon' }),
		buildAction(EVENT_ARROW, 144000, { seats: ['BL4'] }),
		buildAction(EVENT_TIMER_RESET, 144000, { ms: 30000 }),
		buildAction(EVENT_SEAT, 172000, { seat: 'BL4', hero: 'lol/Kennen' }),
		buildAction(EVENT_ARROW, 172000, { seats: ['BR5'] }),
		buildAction(EVENT_TIMER_RESET, 172000, { ms: 30000 }),
		buildAction(EVENT_SEAT, 194000, { seat: 'BR5', hero: 'lol/Qiyana' }),
		buildAction(EVENT_ARROW, 194000, { seats: ['BL5'] }),
		buildAction(EVENT_TIMER_RESET, 194000, { ms: 30000 }),
		buildAction(EVENT_SEAT, 199000, { seat: 'BL5', hero: 'lol/Leona' }),
		buildAction(EVENT_ARROW, 199000, { seats: ['PR4'] }),
		buildAction(EVENT_TIMER_RESET, 199000, { ms: 30000 }),
		buildAction(EVENT_SEAT, 214000, { seat: 'PR4', hero: 'lol/Graves' }),
		buildAction(EVENT_COMPLETE, 223000, { seats: ['PR4'] }),
		buildAction(EVENT_ARROW, 223000, { seats: ['PL4'] }),
		buildAction(EVENT_TIMER_RESET, 223000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 224000, { seats: ['PR4'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 229000, { seats: ['PR4'] }),
		buildAction(EVENT_SEAT, 236000, { seat: 'PL4', hero: 'lol/Trundle' }),
		buildAction(EVENT_SEAT, 250000, { seat: 'PL4', hero: 'lol/Yasuo' }),
		buildAction(EVENT_COMPLETE, 252000, { seats: ['PL4'] }),
		buildAction(EVENT_ARROW, 252000, { seats: ['PL5'] }),
		buildAction(EVENT_TIMER_RESET, 252000, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 254000, { seats: ['PL4'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 258000, { seats: ['PL4'] }),
		buildAction(EVENT_SEAT, 264000, { seat: 'PL5', hero: 'lol/XinZhao' }),
		buildAction(EVENT_COMPLETE, 264500, { seats: ['PL5'] }),
		buildAction(EVENT_ARROW, 264500, { seats: ['PR5'] }),
		buildAction(EVENT_TIMER_RESET, 264500, { ms: 30000 }),
		buildAction(EVENT_SEAT_INFO_SHOW, 267000, { seats: ['PL4'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 272000, { seats: ['PL4'] }),
		buildAction(EVENT_SEAT, 269000, { seat: 'PR5', hero: 'lol/Alistar' }),
		buildAction(EVENT_SEAT, 278000, { seat: 'PR5', hero: 'lol/Rell' }),
		buildAction(EVENT_SEAT, 292000, { seat: 'PR5', hero: 'lol/Zilean' }),
		buildAction(EVENT_COMPLETE, 293000, { seats: ['PR5'] }),
		buildAction(EVENT_ARROW, 293000, { seats: [] }),
		buildAction(EVENT_TIMER_RESET, 293000, { ms: 60000 }),
		buildAction(EVENT_SEAT, 297000, { seat: 'PL2', hero: 'lol/Yasuo' }),
		buildAction(EVENT_SEAT, 297000, { seat: 'PL4', hero: 'lol/Ziggs' }),
		buildAction(EVENT_SEAT, 298000, { seat: 'PL1', hero: 'lol/Rakan' }),
		buildAction(EVENT_SEAT, 298000, { seat: 'PL3', hero: 'lol/Leblanc' }),
		buildAction(EVENT_SEAT, 298000, { seat: 'PR1', hero: 'lol/Graves' }),
		buildAction(EVENT_SEAT, 298000, { seat: 'PR4', hero: 'lol/Jhin' }),
		buildAction(EVENT_SEAT, 302000, { seat: 'PL1', hero: 'lol/Yasuo' }),
		buildAction(EVENT_SEAT, 302000, { seat: 'PL2', hero: 'lol/Rakan' }),
		buildAction(EVENT_SEAT, 305000, { seat: 'PL5', hero: 'lol/Rakan' }),
		buildAction(EVENT_SEAT, 305000, { seat: 'PL2', hero: 'lol/XinZhao' }),
		buildAction(EVENT_SEAT_INFO_SHOW, 309000, { seats: ['PR5'] }),
		buildAction(EVENT_SEAT_INFO_HIDE, 313000, { seats: ['PR5'] }),
	];
}

/**
 * BP 的指令父类
 */
class Action {
	/**
	 * @param {string} name
	 * @param {number} ms
	 * @param {any} arg
	 */
	constructor(name, ms = 0, arg = null) {
		this.name = name;
		this.ms = ms;
		this.arg = arg;
	}
	/** @param {BanPick} bp */
	handle(bp) {
		bp.context.releaseEvent(this.name, this.arg);
	}
}

class SeatPickShowAction extends Action {
	/** @param {string[]} seats */
	constructor(seats, time) {
		super(EVENT_PICK_SHOW, time, {seats});
		this.seats = seats;
	}
	/** @param {BanPick} bp */
	handle(bp) {
		this.seats.forEach(seat => {bp.seatShow[seat] = true});
		super.handle(bp);
	}
}
class SeatPickHideAction extends Action {
	/** @param {string[]} seats */
	constructor(seats, time) {
		super(EVENT_PICK_HIDE, time, {seats});
		this.seats = seats;
	}
	/** @param {BanPick} bp */
	handle(bp) {
		this.seats.forEach(seat => {bp.seatShow[seat] = false});
		super.handle(bp);
	}
}

class ArrowAction extends Action {
	/** @param {string[]} seats 指哪些位置 */
	constructor(seats, time) {
		super(EVENT_ARROW, time, {seats});
		this.seats = seats;
	}
	/** @param {BanPick} bp */
	handle(bp) {
		bp.seatArrow.splice(0, bp.seatArrow.length, ...this.seats);
		super.handle(bp);
	}
}
class SelectAction extends Action {
	/**
	 * @param {string} seat 哪个位置
	 * @param {string} hero 选了谁
	 */
	constructor(seat, hero, time) {
		super(EVENT_SEAT, time, {seat, hero});
		this.seat = seat;
		this.hero = hero;
	}
	/** @param {BanPick} bp */
	handle(bp) {
		bp.seatSelect[this.seat] = this.hero;
		super.handle(bp);
	}
}

export {
	BanPick, Replayer, Action, buildAction
};
